#include <iostream>

using namespace std;

#include "define.h"

void onMouseOut(PMouseEvent me, BYTE& canMove){
    printf("sender point=%p\n \tx=%d, y=%d\n", me->sender, me->p.x, me->p.y);
}

//两个参数的意义假设：
//  PMouseInfo 存储当时的鼠标位置信息
//  canMove 鼠标能否移动，这个是引用，由客户决定是否允许移动鼠标
//这个函数指针业务的意义大概是这样：主程序在鼠标移动前会调用客户挂载的这个函数指针，客户根据传入的鼠标事件对象PMouseEvent进行判断处理，并得出是否允许移动鼠标的决定，
//然后主程序根据客户的指示canMove决定是否移动鼠标

class Screen{
    private:
        int x;
        int y;

        MouseEvent fMe;

    public:
        /* 注意这样注释掉，或者干脆不写这段，就相当于隐藏了默认的构造函数
            就不能这样实例化Screen了：Screen sc = new Screen();
        Screen(){
        }
        */

        Screen(int x, int y): x(0), y(0){
            /* 简单的函数通过在构造函数中初始化也可以，比如这样写构造函数：
               Screen(int x, int y): x(0), y(0)
            和下面的写法等效
            this->x = x;
            this->y = y;
            */

           fMe.sender = this; //screen的指针           
        }

    public:
        void mouseOut(int x, int y){
            this->fMe.p.x = x;
            this->fMe.p.y = y;

            BYTE b = 1;
            this->pmo(&this->fMe, b);
        }

    public:
        /**
         * 鼠标移动事件事件对象，其实就是个函数指针
         */
        onMouseOutEventFunc pmo;
};

/* 这里是函数指针onMouseOutEventFunc同样签名的一个函数实例
*/
void mouseOut(PMouseEvent pm, BYTE& canMove){
    printf("触发对象的地址=%p, x=%d, y=%d\n", pm->sender, pm->p.x, pm->p.y);
}


int main() {
    cout << "typeof之旅==================" << endl;

    //这里创建个Screen的对象，相当于主线程捕获到鼠标移动后需要通知的其中的一个对象        
    Screen *psc = new Screen(0,0);
    printf("psc的地址=%p\n", psc);

    //给Screen的实例挂载鼠标移动事件处理函数
    psc->pmo = mouseOut;
    
    //这里假设主控线程捕获到的屏幕的x和y如下：判定为发生了变化，需要通知监听的对象screen
    //课后作业：操作系统的中断了解下，操作系统是各种中断驱动的，也可以这样说。
    //这里的鼠标事件，其实就是操作系统的IO引起的IO中断
    int x = 356;
    int y = 123; 
    
    //主线程判断到鼠标移动了，需要通知注册了鼠标移动监听事件的全部监听对象
    //一般会创建一个队列，存储注册了监听事件的所有对象的实例的指针，发生事件的时候挨个通知（通知就是调用挂载在各个对象的函数指针onMouseOutEventFunc）
    //这里通知psc实例
    psc->mouseOut(x, y);

    delete psc;

    return 0;
}
